#include "database_widgets.h"
#include "parser_list.h"
#include "window.h"
#include "widget.h"
#include "button.h"
#include "column.h"
#include "row.h"
#include "textbox.h"
#include "label.h"
#include <fstream>

using namespace krryn::gui;
using namespace krryn::parsers;

#define FACTORY_HELPER(OP) \
	OP(window) \
	OP(button) \
	OP(textbox) \
	OP(column) \
	OP(row) \
	OP(label) \

#define CREATE_OF_TYPE(TYPE)				\
	if(a_ClassName == #TYPE){				\
	return TYPE ## ::with(a_Props)		\
		.parent(a_Parent)				\
	.create();				\
	}										\

#define RESERVE(TYPE) reserve(#TYPE);

database_widgets::database_widgets(widget_factory &a_Factory) 
: database_list(), m_Factory(a_Factory){
	m_PathSeparator = ".";
	m_BeginOfPath = "";
}

void database_widgets::load_reserved(){
	FACTORY_HELPER(RESERVE);
}

void database_widgets::build(parser_list_node &a_Entry){
	add_tree_to_database(a_Entry);
	build_ui(a_Entry, 0);
}

void database_widgets::build_ui(parser_list_node &a_Node, widget* a_Parent){
	std::string l_Path = node_to_path(&a_Node);

	widget* l_Widget = m_Factory.create_widget(
		m_PathToClass[l_Path],
		m_PathToProperties[l_Path],
		a_Parent
		);

	m_PathToWidget[l_Path] = l_Widget;

	if(!l_Widget){
		throw invalid_widget_node(a_Node.m_Name);
	}

	for(std::vector<parser_list_node*>::iterator i = a_Node.m_Children.begin();
		i != a_Node.m_Children.end(); i++){
			build_ui(**i, l_Widget);
	}
}

void database_widgets::reserve(const std::string &a_Name){
	m_Reserved[a_Name] = new parser_list_node(a_Name);
	// TODO: create factory
}

void database_widgets::add_tree_to_database(parser_list_node &a_Node){
	if(m_Reserved[a_Node.m_Name]){
		// we're using the reserved names as-is
		add_node_to_database(a_Node, a_Node.m_Name);
	}else{
		// we've created a derived class, check if the prototype
		// leads to a reserved name
		parser_list_node *l_Proto = get_proto_root(&a_Node);
		if(l_Proto && m_Reserved[l_Proto->m_Name]){
			add_node_to_database(a_Node, l_Proto->m_Name);
		}else{
			throw invalid_widget_node(a_Node.m_Name);
		}
	}
}

void database_widgets::add_node_to_database(parser_list_node &a_Node, const std::string &a_ClassName){
	std::string l_Path = node_to_path(&a_Node);

	m_PathToClass[l_Path] = a_ClassName;

	add_properties_to_database(a_Node, l_Path);

	// recurse into children
	for(std::vector<parser_list_node*>::iterator i = a_Node.m_Children.begin();
		i != a_Node.m_Children.end(); i++){
			add_tree_to_database(**i);
	}
}

void database_widgets::add_properties_to_database(parser_list_node &a_Node, const std::string &a_Path){
	typedef std::map<std::string, std::vector<std::string>* > pt;

	// This gets inserted in the database:
	//
	// Example: .root.object.name = value1
	//          .root.object.name = value2
	//          .root.object.other = x
	//
	// when this is the text file (or 'DOM' whatever):
	//
	// object ->
	//		name: value1, value2
	//		other: x
	// 
	for(pt::iterator i = a_Node.m_Properties.begin(); 
		i != a_Node.m_Properties.end(); i++){

			if(i->second){
				for(std::vector<std::string>::iterator j = i->second->begin(); 
					j != i->second->end(); j++){

						m_PathToProperties[a_Path].insert(i->first, *j);
				}
			}
	}
}

std::string database_widgets::node_to_path(parser_list_node *a_Node){
	if(!a_Node) return m_BeginOfPath;
	else return node_to_path(a_Node->m_Parent) + m_PathSeparator + a_Node->m_Name;
}

widget* database_widgets::get_widget(const std::string &a_Path){
	return m_PathToWidget[a_Path];
}


widget* widget_factory::create_widget(const std::string &a_ClassName, const properties &a_Props, widget* a_Parent) const{
	FACTORY_HELPER(CREATE_OF_TYPE);

	return 0;
}

namespace krryn{
namespace gui{

database_widgets& load_ui(std::istream &a_Stream, bool a_PrettyPrint){
	parser_parse_list l_List;
	l_List.parse(a_Stream);

	if(a_PrettyPrint){
		pretty_print_list().print(*l_List.get_root());
	}

	database_widgets *l_Database = new database_widgets(widget_factory());
	l_Database->load(*l_List.get_root());
	return *l_Database;
}

database_widgets& load_ui(const std::string &a_Filename, bool a_PrettyPrint){
	std::ifstream f(a_Filename.c_str());
	return load_ui(f, a_PrettyPrint);
}

}
}